/*  see copyright notice in squirrel.h */
#ifndef _SQCLOSURE_H_
#define _SQCLOSURE_H_


#define _CALC_CLOSURE_SIZE(func)                                     \
	(sizeof(SQClosure) + (func->_noutervalues * sizeof(SQObjectPtr)) \
			+ (func->_ndefaultparams * sizeof(SQObjectPtr)))

struct SQFunctionProto;
struct SQClass;
struct SQClosure : public CHAINABLE_OBJ
{
private:
	SQClosure(SQSharedState *ss, SQFunctionProto *func)
	{
		_function = func;
		__ObjAddRef(_function);
		_base = NULL;
		INIT_CHAIN();
		ADD_TO_CHAIN(&_ss(this)->_gc_chain, this);
		_env = NULL;
		_root = NULL;
	}

public:
	static SQClosure *Create(
			SQSharedState *ss, SQFunctionProto *func, SQWeakRef *root)
	{
		SQInteger size = _CALC_CLOSURE_SIZE(func);
		SQClosure *nc = (SQClosure *)SQ_MALLOC(size);
		new(nc) SQClosure(ss, func);
		nc->_outervalues = (SQObjectPtr *)(nc + 1);
		nc->_defaultparams = &nc->_outervalues[func->_noutervalues];
		nc->_root = root;
		__ObjAddRef(nc->_root);
		_CONSTRUCT_VECTOR(SQObjectPtr, func->_noutervalues, nc->_outervalues);
		_CONSTRUCT_VECTOR(
				SQObjectPtr, func->_ndefaultparams, nc->_defaultparams);
		return nc;
	}
	void Release()
	{
		SQFunctionProto *f = _function;
		SQInteger size = _CALC_CLOSURE_SIZE(f);
		_DESTRUCT_VECTOR(SQObjectPtr, f->_noutervalues, _outervalues);
		_DESTRUCT_VECTOR(SQObjectPtr, f->_ndefaultparams, _defaultparams);
		__ObjRelease(_function);
		this->~SQClosure();
		sq_vm_free(this, size);
	}
	void SetRoot(SQWeakRef *r)
	{
		__ObjRelease(_root);
		_root = r;
		__ObjAddRef(_root);
	}
	SQClosure *Clone()
	{
		SQFunctionProto *f = _function;
		SQClosure *ret = SQClosure::Create(_opt_ss(this), f, _root);
		ret->_env = _env;
		if(ret->_env)
			__ObjAddRef(ret->_env);
		_COPY_VECTOR(ret->_outervalues, _outervalues, f->_noutervalues);
		_COPY_VECTOR(ret->_defaultparams, _defaultparams, f->_ndefaultparams);
		return ret;
	}
	~SQClosure();

	bool Save(SQVM *v, SQUserPointer up, SQWRITEFUNC write);
	static bool Load(
			SQVM *v, SQUserPointer up, SQREADFUNC read, SQObjectPtr &ret);
#ifndef NO_GARBAGE_COLLECTOR
	void Mark(SQCollectable **chain);
	void Finalize()
	{
		SQFunctionProto *f = _function;
		_NULL_SQOBJECT_VECTOR(_outervalues, f->_noutervalues);
		_NULL_SQOBJECT_VECTOR(_defaultparams, f->_ndefaultparams);
	}
	SQObjectType GetType()
	{
		return OT_CLOSURE;
	}
#endif
	SQWeakRef *_env;
	SQWeakRef *_root;
	SQClass *_base;
	SQFunctionProto *_function;
	SQObjectPtr *_outervalues;
	SQObjectPtr *_defaultparams;
};

//////////////////////////////////////////////
struct SQOuter : public CHAINABLE_OBJ
{

private:
	SQOuter(SQSharedState *ss, SQObjectPtr *outer)
	{
		_valptr = outer;
		_next = NULL;
		INIT_CHAIN();
		ADD_TO_CHAIN(&_ss(this)->_gc_chain, this);
	}

public:
	static SQOuter *Create(SQSharedState *ss, SQObjectPtr *outer)
	{
		SQOuter *nc = (SQOuter *)SQ_MALLOC(sizeof(SQOuter));
		new(nc) SQOuter(ss, outer);
		return nc;
	}
	~SQOuter()
	{
		REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain, this);
	}

	void Release()
	{
		this->~SQOuter();
		sq_vm_free(this, sizeof(SQOuter));
	}

#ifndef NO_GARBAGE_COLLECTOR
	void Mark(SQCollectable **chain);
	void Finalize()
	{
		_value.Null();
	}
	SQObjectType GetType()
	{
		return OT_OUTER;
	}
#endif

	SQObjectPtr *_valptr; /* pointer to value on stack, or _value below */
	SQInteger _idx;		  /* idx in stack array, for relocation */
	SQObjectPtr _value;	  /* value of outer after stack frame is closed */
	SQOuter *_next;		  /* pointer to next outer when frame is open   */
};

//////////////////////////////////////////////
struct SQGenerator : public CHAINABLE_OBJ
{
	enum SQGeneratorState
	{
		eRunning,
		eSuspended,
		eDead
	};

private:
	SQGenerator(SQSharedState *ss, SQClosure *closure)
	{
		_closure = closure;
		_state = eRunning;
		_ci._generator = NULL;
		INIT_CHAIN();
		ADD_TO_CHAIN(&_ss(this)->_gc_chain, this);
	}

public:
	static SQGenerator *Create(SQSharedState *ss, SQClosure *closure)
	{
		SQGenerator *nc = (SQGenerator *)SQ_MALLOC(sizeof(SQGenerator));
		new(nc) SQGenerator(ss, closure);
		return nc;
	}
	~SQGenerator()
	{
		REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain, this);
	}
	void Kill()
	{
		_state = eDead;
		_stack.resize(0);
		_closure.Null();
	}
	void Release()
	{
		sq_delete(this, SQGenerator);
	}

	bool Yield(SQVM *v, SQInteger target);
	bool Resume(SQVM *v, SQObjectPtr &dest);
#ifndef NO_GARBAGE_COLLECTOR
	void Mark(SQCollectable **chain);
	void Finalize()
	{
		_stack.resize(0);
		_closure.Null();
	}
	SQObjectType GetType()
	{
		return OT_GENERATOR;
	}
#endif
	SQObjectPtr _closure;
	SQObjectPtrVec _stack;
	SQVM::CallInfo _ci;
	ExceptionsTraps _etraps;
	SQGeneratorState _state;
};

#define _CALC_NATVIVECLOSURE_SIZE(noutervalues) \
	(sizeof(SQNativeClosure) + (noutervalues * sizeof(SQObjectPtr)))

struct SQNativeClosure : public CHAINABLE_OBJ
{
private:
	SQNativeClosure(SQSharedState *ss, SQFUNCTION func)
	{
		_function = func;
		INIT_CHAIN();
		ADD_TO_CHAIN(&_ss(this)->_gc_chain, this);
		_env = NULL;
	}

public:
	static SQNativeClosure *Create(
			SQSharedState *ss, SQFUNCTION func, SQInteger nouters)
	{
		SQInteger size = _CALC_NATVIVECLOSURE_SIZE(nouters);
		SQNativeClosure *nc = (SQNativeClosure *)SQ_MALLOC(size);
		new(nc) SQNativeClosure(ss, func);
		nc->_outervalues = (SQObjectPtr *)(nc + 1);
		nc->_noutervalues = nouters;
		_CONSTRUCT_VECTOR(SQObjectPtr, nc->_noutervalues, nc->_outervalues);
		return nc;
	}
	SQNativeClosure *Clone()
	{
		SQNativeClosure *ret = SQNativeClosure::Create(
				_opt_ss(this), _function, _noutervalues);
		ret->_env = _env;
		if(ret->_env)
			__ObjAddRef(ret->_env);
		ret->_name = _name;
		_COPY_VECTOR(ret->_outervalues, _outervalues, _noutervalues);
		ret->_typecheck.copy(_typecheck);
		ret->_nparamscheck = _nparamscheck;
		return ret;
	}
	~SQNativeClosure()
	{
		__ObjRelease(_env);
		REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain, this);
	}
	void Release()
	{
		SQInteger size = _CALC_NATVIVECLOSURE_SIZE(_noutervalues);
		_DESTRUCT_VECTOR(SQObjectPtr, _noutervalues, _outervalues);
		this->~SQNativeClosure();
		sq_free(this, size);
	}

#ifndef NO_GARBAGE_COLLECTOR
	void Mark(SQCollectable **chain);
	void Finalize()
	{
		_NULL_SQOBJECT_VECTOR(_outervalues, _noutervalues);
	}
	SQObjectType GetType()
	{
		return OT_NATIVECLOSURE;
	}
#endif
	SQInteger _nparamscheck;
	SQIntVec _typecheck;
	SQObjectPtr *_outervalues;
	SQUnsignedInteger _noutervalues;
	SQWeakRef *_env;
	SQFUNCTION _function;
	SQObjectPtr _name;
};


#endif //_SQCLOSURE_H_
